2012年11月30日金曜日
ClassLoaderLogManager
* Per classloader LogManager implementation.
*/
public class ClassLoaderLogManager extends LogManager {
private final class Cleaner extends Thread {
@Override
public void run() {
if (useShutdownHook) {
shutdown();
}
}
}
// ------------------------------------------------------------Constructors
public ClassLoaderLogManager() {
super();
try {
Runtime.getRuntime().addShutdownHook(new Cleaner());
} catch (IllegalStateException ise) {
// We are probably already being shutdown. Ignore this error.
}
}
// -------------------------------------------------------------- Variables
/**
* Map containing the classloader information, keyed per classloader. A
* weak hashmap is used to ensure no classloader reference is leaked from
* application redeployment.
*/
protected final Map<ClassLoader, ClassLoaderLogInfo> classLoaderLoggers =
new WeakHashMap<ClassLoader, ClassLoaderLogInfo>();
/**
* This prefix is used to allow using prefixes for the properties names
* of handlers and their subcomponents.
*/
protected ThreadLocal<String> prefix = new ThreadLocal<String>();
/**
* Determines if the shutdown hook is used to perform any necessary
* clean-up such as flushing buffered handlers on JVM shutdown. Defaults to
* <code>true</code> but may be set to false if another component ensures
* that {@link #shutdown()} is called.
*/
protected volatile boolean useShutdownHook = true;
// ------------------------------------------------------------- Properties
public boolean isUseShutdownHook() {
return useShutdownHook;
}
public void setUseShutdownHook(boolean useShutdownHook) {
this.useShutdownHook = useShutdownHook;
}
// --------------------------------------------------------- Public Methods
/**
* Add the specified logger to the classloader local configuration.
*
* @param logger The logger to be added
*/
@Override
public synchronized boolean addLogger(final Logger logger) {
final String loggerName = logger.getName();
ClassLoader classLoader =
Thread.currentThread().getContextClassLoader();
ClassLoaderLogInfo info = getClassLoaderInfo(classLoader);
if (info.loggers.containsKey(loggerName)) {
return false;
}
info.loggers.put(loggerName, logger);
// Apply initial level for new logger
final String levelString = getProperty(loggerName + ".level");
if (levelString != null) {
try {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
logger.setLevel(Level.parse(levelString.trim()));
return null;
}
});
} catch (IllegalArgumentException e) {
// Leave level set to null
}
}
// Always instantiate parent loggers so that
// we can control log categories even during runtime
int dotIndex = loggerName.lastIndexOf('.');
while (dotIndex >= 0) {
final String parentName = loggerName.substring(0, dotIndex);
Logger.getLogger(parentName);
dotIndex = loggerName.lastIndexOf('.', dotIndex - 1);
}
// Find associated node
LogNode node = info.rootNode.findNode(loggerName);
node.logger = logger;
// Set parent logger
Logger parentLogger = node.findParentLogger();
if (parentLogger != null) {
doSetParentLogger(logger, parentLogger);
}
// Tell children we are their new parent
node.setParentLogger(logger);
// Add associated handlers, if any are defined using the .handlers property.
// In this case, handlers of the parent logger(s) will not be used
String handlers = getProperty(loggerName + ".handlers");
if (handlers != null) {
logger.setUseParentHandlers(false);
StringTokenizer tok = new StringTokenizer(handlers, ",");
while (tok.hasMoreTokens()) {
String handlerName = (tok.nextToken().trim());
Handler handler = null;
ClassLoader current = classLoader;
while (current != null) {
info = classLoaderLoggers.get(current);
if (info != null) {
handler = info.handlers.get(handlerName);
if (handler != null) {
break;
}
}
current = current.getParent();
}
if (handler != null) {
logger.addHandler(handler);
}
}
}
// Parse useParentHandlers to set if the logger should delegate to its parent.
// Unlike java.util.logging, the default is to not delegate if a list of handlers
// has been specified for the logger.
String useParentHandlersString = getProperty(loggerName + ".useParentHandlers");
if (Boolean.valueOf(useParentHandlersString).booleanValue()) {
logger.setUseParentHandlers(true);
}
return true;
}
/**
* Get the logger associated with the specified name inside
* the classloader local configuration. If this returns null,
* and the call originated for Logger.getLogger, a new
* logger with the specified name will be instantiated and
* added using addLogger.
*
* @param name The name of the logger to retrieve
*/
@Override
public synchronized Logger getLogger(final String name) {
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
return getClassLoaderInfo(classLoader).loggers.get(name);
}
/**
* Get an enumeration of the logger names currently defined in the
* classloader local configuration.
*/
@Override
public synchronized Enumeration<String> getLoggerNames() {
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
return Collections.enumeration(getClassLoaderInfo(classLoader).loggers.keySet());
}
/**
* Get the value of the specified property in the classloader local
* configuration.
*
* @param name The property name
*/
@Override
public String getProperty(String name) {
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
String prefix = this.prefix.get();
if (prefix != null) {
name = prefix + name;
}
ClassLoaderLogInfo info = getClassLoaderInfo(classLoader);
String result = info.props.getProperty(name);
// If the property was not found, and the current classloader had no
// configuration (property list is empty), look for the parent classloader
// properties.
if ((result == null) && (info.props.isEmpty())) {
ClassLoader current = classLoader.getParent();
while (current != null) {
info = classLoaderLoggers.get(current);
if (info != null) {
result = info.props.getProperty(name);
if ((result != null) || (!info.props.isEmpty())) {
break;
}
}
current = current.getParent();
}
if (result == null) {
result = super.getProperty(name);
}
}
// Simple property replacement (mostly for folder names)
if (result != null) {
result = replace(result);
}
return result;
}
@Override
public void readConfiguration()
throws IOException, SecurityException {
checkAccess();
readConfiguration(Thread.currentThread().getContextClassLoader());
}
@Override
public void readConfiguration(InputStream is)
throws IOException, SecurityException {
checkAccess();
reset();
readConfiguration(is, Thread.currentThread().getContextClassLoader());
}
@Override
public void reset() throws SecurityException {
Thread thread = Thread.currentThread();
if (thread.getClass().getName().startsWith(
"java.util.logging.LogManager$")) {
// Ignore the call from java.util.logging.LogManager.Cleaner,
// because we have our own shutdown hook
return;
}
ClassLoader classLoader = thread.getContextClassLoader();
ClassLoaderLogInfo clLogInfo = getClassLoaderInfo(classLoader);
resetLoggers(clLogInfo);
super.reset();
}
/**
* Shuts down the logging system.
*/
public void shutdown() {
// The JVM is being shutdown. Make sure all loggers for all class
// loaders are shutdown
for (ClassLoaderLogInfo clLogInfo : classLoaderLoggers.values()) {
resetLoggers(clLogInfo);
}
}
// -------------------------------------------------------- Private Methods
private void resetLoggers(ClassLoaderLogInfo clLogInfo) {
// This differs from LogManager#resetLogger() in that we close not all
// handlers of all loggers, but only those that are present in our
// ClassLoaderLogInfo#handlers list. That is because our #addLogger(..)
// method can use handlers from the parent class loaders, and closing
// handlers that the current class loader does not own would be not
// good.
synchronized (clLogInfo) {
for (Logger logger : clLogInfo.loggers.values()) {
Handler[] handlers = logger.getHandlers();
for (Handler handler : handlers) {
logger.removeHandler(handler);
}
}
for (Handler handler : clLogInfo.handlers.values()) {
try {
handler.close();
} catch (Exception e) {
// Ignore
}
}
clLogInfo.handlers.clear();
}
}
// ------------------------------------------------------ Protected Methods
/**
* Retrieve the configuration associated with the specified classloader. If
* it does not exist, it will be created.
*
* @param classLoader The classloader for which we will retrieve or build the
* configuration
*/
protected ClassLoaderLogInfo getClassLoaderInfo(ClassLoader classLoader) {
if (classLoader == null) {
classLoader = ClassLoader.getSystemClassLoader();
}
ClassLoaderLogInfo info = classLoaderLoggers.get(classLoader);
if (info == null) {
final ClassLoader classLoaderParam = classLoader;
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
try {
readConfiguration(classLoaderParam);
} catch (IOException e) {
// Ignore
}
return null;
}
});
info = classLoaderLoggers.get(classLoader);
}
return info;
}
/**
* Read configuration for the specified classloader.
*
* @param classLoader
* @throws IOException Error
*/
protected void readConfiguration(ClassLoader classLoader)
throws IOException {
InputStream is = null;
// Special case for URL classloaders which are used in containers:
// only look in the local repositories to avoid redefining loggers 20 times
try {
if ((classLoader instanceof URLClassLoader)
&& (((URLClassLoader) classLoader).findResource("logging.properties") != null)) {
is = classLoader.getResourceAsStream("logging.properties");
}
} catch (AccessControlException ace) {
// No permission to configure logging in context
// Log and carry on
ClassLoaderLogInfo info = classLoaderLoggers.get(ClassLoader.getSystemClassLoader());
if (info != null) {
Logger log = info.loggers.get("");
if (log != null) {
Permission perm = ace.getPermission();
if (perm instanceof FilePermission && perm.getActions().equals("read")) {
log.warning("Reading " + perm.getName() + " is not permitted. See \"per context logging\" in the default catalina.policy file.");
}
else {
log.warning("Reading logging.properties is not permitted in some context. See \"per context logging\" in the default catalina.policy file.");
log.warning("Original error was: " + ace.getMessage());
}
}
}
}
if ((is == null) && (classLoader == ClassLoader.getSystemClassLoader())) {
String configFileStr = System.getProperty("java.util.logging.config.file");
if (configFileStr != null) {
try {
is = new FileInputStream(replace(configFileStr));
} catch (IOException e) {
// Ignore
}
}
// Try the default JVM configuration
if (is == null) {
File defaultFile = new File(new File(System.getProperty("java.home"), "lib"),
"logging.properties");
try {
is = new FileInputStream(defaultFile);
} catch (IOException e) {
// Critical problem, do something ...
}
}
}
Logger localRootLogger = new RootLogger();
if (is == null) {
// Retrieve the root logger of the parent classloader instead
ClassLoader current = classLoader.getParent();
ClassLoaderLogInfo info = null;
while (current != null && info == null) {
info = getClassLoaderInfo(current);
current = current.getParent();
}
if (info != null) {
localRootLogger.setParent(info.rootNode.logger);
}
}
ClassLoaderLogInfo info =
new ClassLoaderLogInfo(new LogNode(null, localRootLogger));
classLoaderLoggers.put(classLoader, info);
if (is != null) {
readConfiguration(is, classLoader);
}
addLogger(localRootLogger);
}
/**
* Load specified configuration.
*
* @param is InputStream to the properties file
* @param classLoader for which the configuration will be loaded
* @throws IOException If something wrong happens during loading
*/
protected void readConfiguration(InputStream is, ClassLoader classLoader)
throws IOException {
ClassLoaderLogInfo info = classLoaderLoggers.get(classLoader);
try {
info.props.load(is);
} catch (IOException e) {
// Report error
System.err.println("Configuration error");
e.printStackTrace();
} finally {
try {
is.close();
} catch (Throwable t) {}
}
// Create handlers for the root logger of this classloader
String rootHandlers = info.props.getProperty(".handlers");
String handlers = info.props.getProperty("handlers");
Logger localRootLogger = info.rootNode.logger;
if (handlers != null) {
StringTokenizer tok = new StringTokenizer(handlers, ",");
while (tok.hasMoreTokens()) {
String handlerName = (tok.nextToken().trim());
String handlerClassName = handlerName;
String prefix = "";
if (handlerClassName.length() <= 0) {
continue;
}
// Parse and remove a prefix (prefix start with a digit, such as
// "10WebappFooHanlder.")
if (Character.isDigit(handlerClassName.charAt(0))) {
int pos = handlerClassName.indexOf('.');
if (pos >= 0) {
prefix = handlerClassName.substring(0, pos + 1);
handlerClassName = handlerClassName.substring(pos + 1);
}
}
try {
this.prefix.set(prefix);
Handler handler =
(Handler) classLoader.loadClass(handlerClassName).newInstance();
// The specification strongly implies all configuration should be done
// during the creation of the handler object.
// This includes setting level, filter, formatter and encoding.
this.prefix.set(null);
info.handlers.put(handlerName, handler);
if (rootHandlers == null) {
localRootLogger.addHandler(handler);
}
} catch (Exception e) {
// Report error
System.err.println("Handler error");
e.printStackTrace();
}
}
}
}
/**
* Set parent child relationship between the two specified loggers.
*
* @param logger
* @param parent
*/
protected static void doSetParentLogger(final Logger logger,
final Logger parent) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
logger.setParent(parent);
return null;
}
});
}
/**
* System property replacement in the given string.
*
* @param str The original string
* @return the modified string
*/
protected String replace(String str) {
String result = str;
int pos_start = result.indexOf("${");
if (pos_start != -1) {
int pos_end = result.indexOf('}');
if (pos_end != -1) {
String propName = result.substring(pos_start + 2, pos_end);
String replacement = System.getProperty(propName);
if (replacement != null) {
if(pos_start >0) {
result = result.substring(0,pos_start) +
replacement + replace(result.substring(pos_end + 1));
} else {
result = replacement + replace(result.substring(pos_end + 1));
}
}
}
}
return result;
}
// ---------------------------------------------------- LogNode Inner Class
protected static final class LogNode {
Logger logger;
protected final Map<String, LogNode> children =
new HashMap<String, LogNode>();
protected final LogNode parent;
LogNode(final LogNode parent, final Logger logger) {
this.parent = parent;
this.logger = logger;
}
LogNode(final LogNode parent) {
this(parent, null);
}
LogNode findNode(String name) {
LogNode currentNode = this;
if (logger.getName().equals(name)) {
return this;
}
while (name != null) {
final int dotIndex = name.indexOf('.');
final String nextName;
if (dotIndex < 0) {
nextName = name;
name = null;
} else {
nextName = name.substring(0, dotIndex);
name = name.substring(dotIndex + 1);
}
LogNode childNode = currentNode.children.get(nextName);
if (childNode == null) {
childNode = new LogNode(currentNode);
currentNode.children.put(nextName, childNode);
}
currentNode = childNode;
}
return currentNode;
}
Logger findParentLogger() {
Logger logger = null;
LogNode node = parent;
while (node != null && logger == null) {
logger = node.logger;
node = node.parent;
}
return logger;
}
void setParentLogger(final Logger parent) {
for (final Iterator<LogNode> iter =
children.values().iterator(); iter.hasNext();) {
final LogNode childNode = iter.next();
if (childNode.logger == null) {
childNode.setParentLogger(parent);
} else {
doSetParentLogger(childNode.logger, parent);
}
}
}
}
// -------------------------------------------- ClassLoaderInfo Inner Class
protected static final class ClassLoaderLogInfo {
final LogNode rootNode;
final Map<String, Logger> loggers = new HashMap<String, Logger>();
final Map<String, Handler> handlers = new HashMap<String, Handler>();
final Properties props = new Properties();
ClassLoaderLogInfo(final LogNode rootNode) {
this.rootNode = rootNode;
}
}
// ------------------------------------------------- RootLogger Inner Class
/**
* This class is needed to instantiate the root of each per classloader
* hierarchy.
*/
protected class RootLogger extends Logger {
public RootLogger() {
super("", null);
}
}
}
Mailログハンドラ初期化エラー/Eclipse WTP
原因を判明すべく色々と調査しました。解決策としては下記のパラメータをTomcatの実行コマンドに渡せば、エラーが解消できました。
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
2012年11月28日水曜日
アクセス権のない場所にあるリソース
実験内容と結果
import java.io.*;
import java.net.*;
public class ResourceTest
{
public static void main(String[] args)throws Exception
{
ClassLoader cl = ResourceTest.class.getClassLoader();
URL url = cl.getResource(args[0]);
System.out.println("URL=" + url);
InputStream ins = cl.getResourceAsStream(args[0]);
ins.close();
}
}
このプログラムは、引数に指定した名前を持つリソースのURLを表示後、InputStreamを取得している。
たとえば、以下のように引数を指定して起動すると、java/lang/Objec.classを読み取ることができる。
java ResourceTest java/lang/Object.class
実行結果
URL=jar:file:/C:/Program%20Files/Java/jre6/lib/rt.jar!/java/lang/Object.class
rt.jarに対するURLを取得できている。
しかし、以下のようにSecurityManagerを設定した場合には、
java -Djava.security.manager ResourceTest java/lang/Object.class
nullが返ってきてアクセスできない。
URL=null
Exception in thread "main" java.lang.NullPointerException
at ResourceTest.main(ResourceTest.java:12)
ClassLoaderクラスのgetResource()メソッドとgetResourceAsStream()メソッドはどちらもnullを返してくる。
結論
SecurityManagerが設定されている場合、java.class.path(およびブートストラップクラスパス)内のリソースをClassLoader.getResource()呼び出し時にアクセスチェックが行われているらしい。
アクセス権違反が検出された場合、AccessControlExceptionは発生せず、単純にnullが返ってくる。
疑問
この動作は仕様なのか?少なくともJDKのドキュメント(ClassLoader (Java Platform SE 6)、SecureClassLoader (Java Platform SE 6)、URLClassLoader (Java Platform SE 6)には記載されていない。
追記)仕様だった。以下の場所に記述がある。
http://www.j2ee.me/javase/ja/6/docs/ja/technotes/guides/lang/resources.html#security
getResource() は、情報へのアクセスを提供するため、セキュリティーの規則が適切に定義および構築されている必要があります。セキュリティー上の配慮により、あるセキュリティーコンテキストであるリソースへのアクセスが許可されていない場合は、getResource() メソッドは、あたかもそのリソースが存在しないかのように失敗する (null を返す) ようになっています。 これは、存在性攻撃に対する配慮です。
Location-Independent Access to Resources( getResourceAsStream return null when custom of SecurityManager )
http://download.java.net/jdk8/docs/technotes/guides/lang/resources.html
Overview
A resource is data (images, audio, text, and so on) that a program needs to access in a way that is independent of the location of the program code. Java programs can use two mechanisms to access resources: Applets use Applet.getCodeBase()
to get the base URL for the applet code and then extend the base URL with a relative path to load the desired resource, for example with Applet.getAudioClip(url)
. Applications use "well known locations" such as System.getProperty("user.home")
or System.getProperty("java.home")
, then add "/lib/resource", and open that file.
Methods in the classes Class
and ClassLoader
provide a location-independent way to locate resources. For example, they enable locating resources for:
- An applet loaded from the Internet using multiple HTTP connections.
- An applet loaded using JAR files.
- A Java Bean loaded or installed in the CLASSPATH.
- A "library" installed in the CLASSPATH.
These methods do not provide specific support for locating localized resources. Localized resources are supported by the internationalization facilities.
Resources, names, and contexts
A resource is identified by a string consisting of a sequence of substrings, delimited by slashes (/), followed by a resource name. Each substring must be a valid Java identifier. The resource name is of the form shortName
or shortName.extension
. Both shortName
and extension
must be Java identifiers.
The name of a resource is independent of the Java implementation; in particular, the path separator is always a slash (/). However, the Java implementation controls the details of how the contents of the resource are mapped into a file, database, or other object containing the actual resource.
The interpretation of a resource name is relative to a class loader instance. Methods implemented by the ClassLoader
class do this interpretation.
System Resources
A system resource is a resource that is either built-in to the system, or kept by the host implementation in, for example, a local file system. Programs access system resources through the ClassLoader
methods getSystemResource
and getSystemResourceAsStream
.
For example, in a particular implementation, locating a system resource may involve searching the entries in the CLASSPATH. The ClassLoader
methods search each directory, ZIP file, or JAR file entry in the CLASSPATH for the resource file, and, if found, returns either an InputStream
, or the resource name. If not found, the methods return null. A resource may be found in a different entry in the CLASSPATH than the location where the class file was loaded.
Non-System Resources
The implementation of getResource
on a class loader depends on the details of the ClassLoader
class. For example, AppletClassLoader
:
- First tries to locate the resource as a system resource; then, if not found,
- Searches through the resources in archives (JAR files) already loaded in this CODEBASE; then, if not found,
- Uses CODEBASE and attempts to locate the resource (which may involve contacting a remote site).
All class loaders will search for a resource first as a system resource, in a manner analogous to searcing for class files. This search rule permits overwriting locally any resource. Clients should choose a resource name that will be unique (using the company or package name as a prefix, for instance).
Resource Names
A common convention for the name of a resource used by a class is to use the fully qualified name of the package of the class, but convert all periods (.) to slashes (/), and add a resource name of the form name.extension
. To support this, and to simplify handling the details of system classes (for which getClassLoader
returns null), the class Class
provides two convenience methods that call the appropriate methods inClassLoader
.
The resource name given to a Class
method may have an initial starting "/" that identifies it as an "absolute" name. Resource names that do not start with a "/" are "relative".
Absolute names are stripped of their starting "/" and are passed, without any further modification, to the appropriate ClassLoader
method to locate the resource. Relative names are modified according to the convention described previously and then are passed to a ClassLoader
method.
Using Methods of java.lang.Class
The Class
class implements several methods for loading resources.
The method getResource()
returns a URL for the resource. The URL (and its representation) is specific to the implementation and the JVM (that is, the URL obtained in one runtime instance may not work in another). Its protocol is usually specific to the ClassLoader
loading the resource. If the resource does not exist or is not visible due to security considerations, the methods return null.
If the client code wants to read the contents of the resource as an InputStream
, it can apply the openStream()
method on the URL. This is common enough to justify adding getResourceAsStream()
to Class
and ClassLoader
. getResourceAsStream()
the same as callinggetResource().openStream()
, except that getResourceAsStream()
catches IO exceptions returns a null InputStream
.
Client code can also request the contents of the resource as an object by applying the java.net.URL.getContent()
method on the URL. This is useful when the resource contains the data for an image, for instance. In the case of an image, the result is an awt.image.ImageProducer
object, not an Image
object.
The getResource
and getResourceAsStream
methods find a resource with a given name. They return null if they do not find a resource with the specified name. The rules for searching for resources associated with a given class are implemented by the class's ClassLoader. The Class
methods delegate to ClassLoader
methods, after applying a naming convention: if the resource name starts with "/", it is used as is. Otherwise, the name of the package is prepended, after converting all periods (.) to slashes (/).
public InputStream getResourceAsStream(String name) { name = resolveName(name); ClassLoader cl = getClassLoader(); if (cl==null) { return ClassLoader.getSystemResourceAsStream(name); // A system class. } return cl.getResourceAsStream(name); } public java.net.URL getResource(String name) { name = resolveName(name); ClassLoader cl = getClassLoader(); if (cl==null) { return ClassLoader.getSystemResource(name); // A system class. } return cl.getResource(name); }
The resolveName
method adds a package name prefix if the name is not absolute, and removes any leading "/" if the name is absolute. It is possible, though uncommon, to have classes in diffent packages sharing the same resource.
private String resolveName(String name) { if (name == null) { return name; } if (!name.startsWith("/")) { Class c = this; while (c.isArray()) { c = c.getComponentType(); } String baseName = c.getName(); int index = baseName.lastIndexOf('.'); if (index != -1) { name = baseName.substring(0, index).replace('.', '/') + "/" + name; } } else { name = name.substring(1); } return name; }
Using Methods of java.lang.ClassLoader
The ClassLoader
class has two sets of methods to access resources. One set returns an InputStream
for the resource. The other set returns a URL. The methods that return an InputStream
are easier to use and will satisfy many needs, while the methods that return URLs provide access to more complex information, such as an Image and an AudioClip.
The ClassLoader
manges resources similarly to the way it manages classes. A ClassLoader
controls how to map the name of a resource to its content. ClassLoader
also provides methods for accessing system resources, analogous to the system classes. The Class
class provides some convenience methods that delegate functionality to the ClassLoader
methods.
Many Java programs will access these methods indirectly through the I18N (localization) APIs. Others will access it through methods in Class
. A few will directly invoke the ClassLoader
methods.
The methods in ClassLoader
use the given String as the name of the resource without applying any absolute/relative transformation (see the methods in Class). The name should not have a leading "/".
System resources are those that are handled by the host implemenation directly. For example, they may be located in the CLASSPATH.
The name of a resource is a "/"-separated sequence of identifiers. The Class
class provides convenience methods for accessing resources; the methods implement a convention where the package name is prefixed to the short name of the resource.
Resources can be accessed as an InputStream
, or a URL.
The getSystemResourceAsStream
method returns an InputStream for the specified system resource or null if it does not find the resource. The resource name may be any system resource.
The getSystemResource
method finds a system resource with the specified name. It returns a URL to the resource or null if it does not find the resource. Calling java.net.URL.getContent()
with the URL will return an object such as ImageProducer
, AudioClip
, or InputStream
.
The getResourceAsStream
method returns an InputStream
for the specified resource or null if it does not find the resource.
The getResource
method finds a resource with the specified name. It returns a URL to the resource or null if it does not find the resource. Callingjava.net.URL.getContent()
with the URL will return an object such as ImageProducer
, AudioClip
, or InputStream
.
Security
Since getResource()
provides access to information, it must have well-defined and well-founded security rules. If security considerations do not allow a resource to be visible in some security context, the getResource()
method will fail (return null) as if the resource were not present at all, this addresses existence attacks.
Class loaders may not provide access to the contents of a .class file for both security and performance reasons. Whether it is possible to obtain a URL for a .class file depends on the specifics, as shown below.
There are no specified security issues or restrictions regarding resources that are found by a non-system class loader. AppletClassLoader
provides access to information that is loaded from a source location, either individually, or in a group through a JAR file; thus AppletClassLoader
should apply the same checkConnect()
rules when dealing with URLs through getResource()
.
The system ClassLoader
provides access to information in the CLASSPATH. A CLASSPATH may contain directories and JAR files. Since a JAR file is created intentionally, it has a different significance than a directory where things may end up in a more casual manner. In particular, we are more strict on getting information out of a directory than out from a JAR file.
If a resource is in a directory:
getResource()
invocations will useFile.exists()
to determine whether to make the corresponding file visible to the user. Recall thatFile.exists()
uses thecheckRead()
method in the security manager.- the same applies to
getResourceAsStream()
.
If the resource is in a JAR file:
getResource()
invocations will succeed for all files, regardless of whether the invocation is done from within a system or a non-system class.getResourceAsStream()
invocations will succeed for non .class resources, and so will forjava.net.URL.getContent()
on corresponding URLs.
Examples
This section provides two examples of client code. The first example uses "absolute resource" names and traditional mechanisms to get a Class
object.
package pkg; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; class Test { private static final String absName = "/pkg/mumble.baf"; public static void test1() { Class c=null; try { c = Class.forName("pkg.Test"); } catch (Exception ex) { // This should not happen. } InputStream s = c.getResourceAsStream(absName); // do something with it. } public void test2() { InputStream s = this.getClass().getResourceAsStream(absName); // do something with it. } }
This example uses "relative resource" names and the mechanism available from the compiler through the -experimental
flag, to get a Class
object.
package pkg; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; class Test { private static final String relName = "mumble.baf"; public static void test1() { InputStream s = Test.class.getResourceAsStream(relName); // do something with it. } public void test2() { InputStream s = Test.class.getResourceAsStream(relName); // do something with it. }
API References
Eclipse: Access restriction: The method createJPEGEncoder(OutputStream) from the type JPEGCodec is not accessible due to restriction on required library
Access restriction: The method createJPEGEncoder(OutputStream) from the type JPEGCodec is not accessible due to restriction on required library /usr/lib/jvm/java-6-sun-1.6.0.24/jre/lib/rt.jar
The fix it:
- Right-click the project, and click "Properties"
- open "Java Compiler",
- select "Errors/Warnings",
- "Enable project specific settings",
- open "Deprecated and restricted API"
- change to "Warning" for "Forbidden reference(access rules)"
2012年11月27日火曜日
PDFBox create pdf from Graphics2d
- add a page
- create a PDJpeg from a BufferedImage of the Graphics2D object
- add the PDJpeg to your page
- save the document
public static void main(String... _) throws Exception {
PDDocument doc = null;
try {
doc = new PDDocument();
PDPage page = new PDPage();
doc.addPage(page);
PDXObjectImage ximage = null;
BufferedImage image = new BufferedImage(300, 300, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
g.drawString("Hallo PDFBox", 100, 100);
g.dispose();
ximage = new PDJpeg(doc, image);
/* Lade ein Bild */
// ximage = new PDJpeg(doc, new FileInputStream( "PFAD" ) );
PDPageContentStream contentStream = new PDPageContentStream(doc,page);
contentStream.drawImage(ximage, 20, 20);
contentStream.close();
doc.save("PFAD WO PDF GESPEICHERT WERDEN SOLL"); //anpassen
} finally {
if (doc != null) {
doc.close();
}
}
}
Java で、ランダムな文字列を生成する方法
java.util.Random
java.security.SecureRandom
の2つがあってRandomクラスは以下のような強度しか持っていない。
Random クラスのインスタンスは、一連の擬似乱数を生成します。クラスでは 48 ビットのシードを使い、このシードは線形合同法で変更されます。
no title
しかも初期化があまりよくなくて初回の乱数がとても偏る。
Random r = new java.util.Random();
double x, min = 1, max = 0;
for (int i = 0; i < 1024; i++) {
r.setSeed(i);
x = r.nextDouble();
if (x > max)
max = x;
if (x < min)
min = x;
}
System.out.println("Min=" + min + " Max=" + max);
Min=0.6753377750582709 Max=0.7669794809891215
こんなプログラムを回してみるとと1000通りの種を渡しても初回生成値が極度に偏ってしまう。
んで、SecureRandomはこのあたりの問題を解消するのでててきた。
for (int i = 0; i < 1024; i++) {
sr.setSeed(i);
x = sr.nextDouble();
if (x > max){
max = x;
}
if (x < min){
min = x;
}
}
BigDecimal bigMin = new BigDecimal(min).setScale(16,BigDecimal.ROUND_HALF_UP);
System.out.println("Min=" + bigMin + " Max=" + max);
x = 0.0;
Min=0.0004351500205996 Max=0.9986417236968099
SecureRandomにすると一気に分散するようになった。
ランダムな文字列を生成するには
そもそもランダムな文字列を作る為にRandomを久々にみてたわけですが、
今普通にランダムな文字列を使うのならcommons-langのRandomStringUtilsで事足りる。
アスキー文字なら
RandomStringUtils.randomAscii(10)
⇒DF|M1!7W=@
アルファベットなら
RandomStringUtils.randomAlphabetic(10)
⇒doKntQIMve
数字なら(0始まりも生成されてしまうので注意)
RandomStringUtils.randomNumeric(10)
⇒8266471697
頭0を嫌うなら
RandomStringUtils.random(1,"123456789") + RandomStringUtils.randomNumeric(9);
自分で指定した文字の中からランダムにしたいのなら
RandomStringUtils.random(10,"012345abcdef");
⇒c021cc0b24
長さもランダムにしたいのならRandomUtils.nextIntと組み合わせて(1〜3文字のランダムな文字が変える)
RandomStringUtils.random(RandomUtils.nextInt(3)+1,"12345678");
⇒1
⇒255
⇒86
Java帳票 iTextで高解像度の画像を印刷する
- Javaの標準の印刷 (JPS) で画像を印刷する手段は一長一短
- iTextで、高解像度の画像をPDFに貼り付けて印刷すれば上手くといいな
ソースコード †
- 1000px x 1000px の画像を PDF に貼り付けた
- 画像の解像度は img.scalePercent(float); で指定できる。 画像を縮小するとその分解像度が上がる
縮尺(%) 解像度(dpi) 100f 72dpi 50f 144dpi 25f 288dpi 10f 720dpi 5f 1440dpi - 無指定時は 72 dpi
- 画像の大きさ(縦横の長さ)が小さくなっても画素情報は失われない。たとえば、 (1inch x 1inch) の (1000px x 1000px) の画像になる。
package com.snail.exam.printexam;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.Document;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfWriter;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.imageio.ImageIO;
public class ITextExam3 {
public static void main(String[] args) {
try {
OutputStream out = new FileOutputStream("/tmp/iTextExample3.pdf");
//ByteArrayOutputStream out = new ByteArrayOutputStream();
Document doc = new Document(PageSize.A4, 50, 50, 50, 50);
PdfWriter pdfwriter = PdfWriter.getInstance(doc, out);
Image img = createImage();
doc.open();
doc.add(new Phrase("72dpi\n\n"));
doc.add(img);
doc.newPage();
img.scalePercent(50f);
doc.add(new Phrase("144dpi\n\n"));
doc.add(img);
doc.newPage();
img.scalePercent(25f);
doc.add(new Phrase("288dpi\n\n"));
doc.add(img);
doc.newPage();
img.scalePercent(10f);
doc.add(new Phrase("720dpi\n\n"));
doc.add(img);
doc.newPage();
img.scalePercent(5f);
doc.add(new Phrase("1440dpi\n\n"));
doc.add(img);
// Document End
doc.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private static Image createImage() throws IOException, BadElementException{
BufferedImage image = new BufferedImage(1000, 1000, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, image.getWidth(), image.getHeight());
g.setColor(Color.GRAY);
for (int x = 0; x < image.getWidth(); x += 5) {
g.drawLine(x, 0, x, image.getHeight());
}
for (int y = 0; y < image.getHeight(); y += 5) {
g.drawLine(0, y, image.getWidth(), y);
}
g.setColor(Color.BLACK);
g.setFont(new java.awt.Font(java.awt.Font.SANS_SERIF, java.awt.Font.PLAIN, 24));
g.drawString("20pt 123 ABC あいう アイウ", 0, 20);
g.setFont(new java.awt.Font(java.awt.Font.SANS_SERIF, java.awt.Font.PLAIN, 10));
g.drawString("10pt 123 ABC あいう アイウ", 0, 40);
g.setFont(new java.awt.Font(java.awt.Font.SANS_SERIF, java.awt.Font.PLAIN, 8));
g.drawString("8pt 123 ABC あいう アイウ", 0, 60);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ImageIO.write(image, "png", out);
return com.itextpdf.text.Image.getInstance(out.getBytes());
}
}
IText Image オブジェクト
- JPEG: Joint Photographic Experts Group. 非可逆な圧縮法として一般的に使われており、96%程度サイズを圧縮する。Web に写真を掲載するためにはもっとも優れている。
- GIF: Graphic Interchange Format. イメージファイルの共通フォーマット、特にイメージの多くの部分が同じ色の場合に使われる。単純なイメージの GIF フォーマットファイルは、JPEG フォーマットのものよりも小さくなることが多いですが、写真は JPEG のようには保存されません。
- PNG: Portable Network Graphics. GIFの後継フォーマットとしてデザインされた。GIF のように、圧縮、透過、プログレッシブローディングをサポートしており、特許の制約からフリーです。
- TIFF: Tagged Image File Format. デジタルスキャナで取り込まれたイメージのための共通フォーマットとして使われている。
- BMP: Windows bitmap. Microsoft Windows においてビットマップファイルの共通フォーマット。他のオペレーティングシステムでは、色に関する事項は十分にはサポートされていない。
- WMF: Windows Metafile Format. Windows コンパチブルコンピュータのベクタグラフィックスフォーマットであり、ワードプロセッサのクリップアートとしてよく使われる。
- EPS の基本的な機能: Encapsulated PostScript. PostScript 言語においてイメージを記述するためのフォーマット。
- java.awt.Image: JAVA オブジェクト。SUNのJDKに含まれている。
- com.lowagie.text.pdf.PdfTemplate: PdfTemplate で定義された PDF 文は、Image のなかにラップすることができます。
- com.lowagie.text.pdf.Barcode: 抽象クラスです。どのようなバーコードがサポートされているかは、実装クラスを参照してください。
Adds different formats of images to a document.: see Images.pdf
External resources for this example: otsoe.jpg iText.bmp pngnow.png iText.tif getacro.gifiText.wmf
Image インスタンスを得るもっとも普通の方法は、ファイルのパス (getInstance(java.lang.String)) か URL (getInstance(java.net.URL)) を使う方法ですが、Raw イメージデータ (例えば、データベースにイメージデータが格納されている場合など) から作成することもできます。
Using raw image data to construct an Image object.: see rawdata.pdf
External resources for this example: otsoe.jpg
Using a java.awt.Image object to construct an Image object.: see awt_image.pdf
External resources for this example: H.gif
Alignment of images.: see alignment.pdf
External resources for this example: vonnegut.gif otsoe.jpg hitchcock.png
Alignment and wrapping of images.: see imagesAlignment.pdf
External resources for this example: vonnegut.gif otsoe.jpg hitchcock.png
writer.setStrictImageSequence(true);例で、iTextの既定の振る舞いと、正確な順序でイメージを配置することを設定した場合の違いを見てください。
Adds images to a document, once respecting the order in which they were added, once in the default order.: see inSequence.pdf NotInSequence.pdf
External resources for this example: otsoe.jpg getacro.gif
Adding an Image at absolute positions.: see absolutepositions.pdf
External resources for this example: hitchcock.png
既定では、スケーリングされていないイメージは、解像度が 72dpi と扱われることに注意。50% でスケーリングされたイメージでは、144 になります。小さくスケーリングされたイメージは、ピクセルの大きさが小さくなるので、解像度は大きくなりますを、300dpi で出力したいときには、72/300=24% のスケーリングを使います。例えば、24% (72/300 = 0.24) のスケーリングで 1500X1500 ピクセル (5X300 = 1500) のイメージを PDF へ配置すると、PDF 中では、1500X1500 ピクセル が 5X5 inch となり解像度が 300dpi のイメージとなります。サイズがどうあろうとも、そのイメージはいつでも 1500X1500 ピクセルです。
Scaling images.: see scaling.pdf
External resources for this example: otsoe.jpg
Make a DVD Cover.: see dvdcover.pdf
External resources for this example: sunflower-front.jpg sunflower-back.jpg
Rotating images.: see rotating.pdf
External resources for this example: otsoe.jpg
images and annotations.: see annotated_images.pdf
External resources for this example: otsoe.jpg iText.wmf
Images wrapped in a Chunk.: see imageChunks.pdf
External resources for this example: pngnow.png
3C | ||||||||
7E | ||||||||
E7 | ||||||||
C3 | ||||||||
C3 | ||||||||
E7 | ||||||||
7E | ||||||||
3C |
byte maskr[] = {(byte)0x3c, (byte)0x7e, (byte)0xe7, (byte)0xc3, (byte)0xc3, (byte)0xe7, (byte)0x7e, (byte)0x3c}; Image mask = Image.getInstance(8, 8, 1, 1, maskr); mask.makeMask(); mask.setInvertMask(true);このマスクを、イメージの一部をクリップするための明示的なマスクとして利用できます。
PdfContentByte cb = writer.getDirectContent(); Image image = Image.getInstance("vonnegut.gif"); image.setImageMask(mask);色とパタンの章では、ステンシルマスクの概念について論じます。Image から作成したマスクは、このようにステンシルマスクにも使えます。
PdfContentByte cb = writer.getDirectContent(); cb.setRGBColorFill(255, 0, 0); cb.addImage(mask, mask.scaledWidth() * 8, 0, 0, mask.scaledHeight() * 8, 100, 400);(addImage メソッドについては、座標系などについての知識も必要です。。)
Applying a mask to an image.: see maskedImages.pdf
External resources for this example: otsoe.jpg